//
//  Envelope.cpp
//  BigDogValueSynth
//
//  Created by Alistair Barker on 20/05/2013.
//  Copyright 2013 __MyCompanyName__. All rights reserved.
//  Version 1.1.0

#include "Envelope.h"

Envelope::Envelope (double attack, double decay, double sustain, double release, double Fs)
{
    setAttack(attack, Fs);
    setDecay(decay, Fs);
    setSustain(sustain);
    setRelease(release, Fs);
    
    position = 0.0f;
    state = kIdle;
} 

double Envelope::getIncValue(double value, double Fs)
{
    
    return (value > 0.001) ? 1.0/(value*Fs) : 1.0/(0.001*Fs);
}

void Envelope::noteOn(float velocity)
{
    gainAtNoteOn = gain;
    state = kAttack;
    position = 0.0f;
}

void Envelope::noteOff()
{
    gainAtNoteOff = gain;
    state = kRelease;
    position = 0.0;
}

float Envelope::tick()
{
    switch (state) {
        case kAttack:
            position += attackInc;                    
            if (position >= 1.0) {
                state = kDecay;
                position = 0.0;
            }
            break;
            
        case kDecay:
            position += decayInc;                    
            if (position >= 1.0) {
                position = 0.0;
                state = kSustain;
                
            }

            break;
            
        case kRelease:
            position += releaseInc;                    
            if (position >= 1.0) {
                state = kIdle;
                position = 0.0;
            }

            break;
            
        case kSustain:
            break;
            
        case kIdle:
            break;
    }
    return position;
}

void Envelope::processEnvGain ()
{
    
    switch (state) {
            
        case kAttack:
            gain = gainAtNoteOn + position*(1-gainAtNoteOn);
            break;
            
        case kDecay:
            gain = 1+(sustainLevel - 1) * position;
            break;
            
        case kSustain:
            gain = sustainLevel;
            break;
            
        case kRelease:
            gain = gainAtNoteOff * (1 - position);
            break; 
            
        case kIdle:
            gain = 0.0;
            break;
    }
}

float Envelope::getLinearGainAndTick()
{
    tick();
    processEnvGain();
    return gain;
}

float Envelope::getLogGainAndTick()
{
    tick();
    processEnvGain();
    return getCurve(gain);
}

double Envelope::getCurve(double value)
{
    const double cutoffGain = pow (2, -10);
    return cutoffGain * pow (2, -value * log2(cutoffGain));
}

void Envelope::setEnvelope(double attack, double decay, double sustain, double release, double Fs)
{
    setAttack(attack, Fs);
    setDecay(attack, Fs);
    setSustain(attack);
    setRelease(attack, Fs);
    
}
